;================================================================
;  wobble.s
;               The Wobbling effect
;
;================================================================
;
; 25thanni, a demo dedicated to the 25th anniversary of the ZX81.
;
; (c)2006 Bodo Wenzel
;
; This program is free software; you can redistribute it and/or
; modify it under the terms of the GNU General Public License as
; published by the Free Software Foundation; either version 2 of
; the License, or (at your option) any later version.
;
; This program is distributed in the hope that it will be useful,
; but WITHOUT ANY WARRANTY; without even the implied warranty of
; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
; GNU General Public License for more details.
;
; You should have received a copy of the GNU General Public
; License along with this program; if not, write to the Free
; Software Foundation Inc., 59 Temple Place, Suite 330, Boston,
; MA 02111-1307 USA
;================================================================

	.module	wobble

;= Externals ====================================================

	.globl	heap_ptr

	.globl	M_TOP,M_BOTTOM
	.globl	check_break
	.globl	set_show
	.globl	show_dummy
	.globl	vsync
	.globl	b_check

	.globl	G0,G1,G2,G3,G4,G5,G6,G7,G8,G9,GA,GB,GC,GD,GE,GF
	.globl	__,X8,X9,XA,QU,PD,DL,CL,QM,LP,RP,GT,LT,EQ,PL,MI
	.globl	TI,SL,SC,CM,PE,_0,_1,_2,_3,_4,_5,_6,_7,_8,_9
	.globl	_A,_B,_C,_D,_E,_F,_G,_H,_I,_J,_K,_L,_M,_N,_O,_P
	.globl	_Q,_R,_S,_T,_U,_V,_W,_X,_Y,_Z,NL,INV

	.globl	HRG_WIDTH,HRG_HEIGHT,PIXEL_PER_CLOCK

	.globl	DELAY_OFFSET
	.globl	delay

	.globl	MATH_SIGN,MATH_TWO_PI
	.globl	sine

	.globl	unpack

;= Constants ====================================================

; double global definition checked by linker
M_TOP		==	46
M_BOTTOM	==	5
HRG_WIDTH	==	256
HRG_HEIGHT	==	192
PIXEL_PER_CLOCK	==	2
MATH_SIGN	==	0x80
MATH_TWO_PI	==	256

BITS_PER_BYTE	=	8
BYTES_PER_WORD	=	2

PIC_WIDTH	=	128
PIC_HEIGHT	=	91
BYTE_WIDTH	=	PIC_WIDTH/BITS_PER_BYTE

V_AMPLITUDE	=	HRG_HEIGHT-PIC_HEIGHT
H_AMPLITUDE	=	(HRG_WIDTH-PIC_WIDTH)/PIXEL_PER_CLOCK

V_MINUS_H	=	V_AMPLITUDE-H_AMPLITUDE
V_H_FILL	=	PIC_HEIGHT-V_MINUS_H
	.if	H_AMPLITUDE&3
	.error	"Code depends on H_AMPLITUDE being multiple of 4"
	.endif
	.if	H_AMPLITUDE/PIC_HEIGHT
	.error	"Code depends on H_AMPLITUDE<PIC_HEIGHT"
	.endif
	.if	H_AMPLITUDE/V_AMPLITUDE
	.error	"Code depends on H_AMPLITUDE<V_AMPLITUDE"
	.endif
	.if	V_MINUS_H/PIC_HEIGHT
	.error	"Code depends on V_MINUS_H<PIC_HEIGHT"
	.endif

; bit numbers and masks of the action flags
S_H_MOVE	=	0
S_V_MOVE	=	1
S_JUMP		=	2
S_SINE		=	3
S_SH_T		=	4
S_SH_B		=	5
S_W_T		=	6
S_W_B		=	7
M_H_MOVE	=	1<<S_H_MOVE
M_V_MOVE	=	1<<S_V_MOVE
M_JUMP		=	1<<S_JUMP
M_SINE		=	1<<S_SINE
M_SH_T		=	1<<S_SH_T
M_SH_B		=	1<<S_SH_B
M_W_T		=	1<<S_W_T
M_W_B		=	1<<S_W_B

; control values for show_hrg
OFFSET_END	=	0
OFFSET_2R	=	1
OFFSET_1R	=	2
OFFSET_0	=	3
OFFSET_1L	=	4
OFFSET_2L	=	5

;= Program code =================================================

	.area	CODE

;- Show the Wobbling effect -------------------------------------

wobble::
	ld	hl,#show_dummy
	call	set_show

	ld	hl,(heap_ptr)
	ld	e,l
	ld	d,h
	inc	de
	ld	(hl),#0
	ld	bc,#PIC_HEIGHT*BYTE_WIDTH-1
	ldir

	ld	a,#M_TOP-2
	ld	(sh_p_top),a
	ld	a,#M_BOTTOM+V_AMPLITUDE
	ld	(sh_p_bottom),a
	ex	de,hl
	ld	(table),hl
	ld	(hl),#0
	inc	hl
	ld	b,#PIC_HEIGHT
w_t_lp:
	ld	(hl),#OFFSET_0
	inc	hl
	djnz	w_t_lp
	ld	(hl),#OFFSET_END; prepare table

	inc	hl
	push	hl
	ld	de,#logo
	ld	bc,#PAC_LG_SIZE
	call	unpack

	ex	de,hl
	ld	(show_hrg),hl
	ld	de,#scroll_line
	ld	bc,#PAC_SL_SIZE
	call	unpack

	ld	hl,#script
	ld	(script_ptr),hl
	call	inc_script
	xor	a
	ld	(sine_h),a
	ld	(sine_v),a
	ld	hl,#wobble_tab
	ld	(wobble_ptr),hl	; set up the controls

	ld	hl,#show_hrg
	call	set_show

	pop	hl
	ld	de,(heap_ptr)
	ld	a,#PIC_HEIGHT
w_ri_lp:
	push	af
	push	hl
	call	vsync
	pop	hl
	pop	af
	ld	bc,#BYTE_WIDTH
	ldir
	dec	a
	jr	nz,w_ri_lp	; roll in logo

w_loop:
	call	vsync

	ld	hl,#state
	ld	c,(hl)		; action flags
	inc	hl
	ld	a,(hl)
	inc	hl
	rra			; for alternating frame control

;- wobbling (beginning at the top)  - - - - - - - - - - - - - - -

	bit	S_W_T,c
	jr	z,w_w_t_skip

	push	bc
	push	hl

	ld	hl,(wobble_ptr)
w_w_t_get:
	ld	a,(hl)
	inc	hl
	ld	(wobble_ptr),hl
	cp	#OFFSET_END
	jr	nz,w_sh_t_insert

	ld	hl,#wobble_tab
	jr	w_w_t_get
w_w_t_skip:

;- shifting (beginning at the top)  - - - - - - - - - - - - - - -

	bit	S_SH_T,c
	jr	z,w_sh_t_skip

	push	bc
	push	hl

	ld	a,(hl)
w_sh_t_insert:
	ld	hl,(table)
	inc	hl
	ld	bc,#PIC_HEIGHT-1
	add	hl,bc
	ld	e,l
	ld	d,h
	dec	hl
	lddr

	ex	de,hl
	ld	(hl),a
	dec	hl
	sub	#OFFSET_0
	add	a,(hl)
	ld	(hl),a

	pop	hl
	pop	bc
w_sh_t_skip:

;- wobbling (beginning at the bottom) - - - - - - - - - - - - - -

	bit	S_W_B,c
	jr	z,w_w_b_skip

	push	bc
	push	hl

	ld	hl,(wobble_ptr)
w_w_b_get:
	ld	a,(hl)
	push	af
	jr	nc,w_w_b_noinc
	inc	hl
w_w_b_noinc:
	ld	(wobble_ptr),hl
	cp	#OFFSET_END
	jr	nz,w_sh_b_insert

	pop	af
	ld	hl,#wobble_tab
	jr	w_w_b_get
w_w_b_skip:

;- shifting (beginning at the bottom) - - - - - - - - - - - - - -

	bit	S_SH_B,c
	jr	z,w_sh_b_skip

	push	bc
	push	hl

	ld	a,(hl)
	push	af
w_sh_b_insert:
	ld	hl,(table)
	ld	a,#OFFSET_0
	inc	hl
	sub	(hl)
	dec	hl
	add	a,(hl)
	ld	(hl),a
	inc	hl

	ld	bc,#PIC_HEIGHT-1
	ld	e,l
	ld	d,h
	inc	hl
	ldir

	pop	af
	ld	(de),a

	pop	hl
	pop	bc
w_sh_b_skip:

;- doing the sine wave  - - - - - - - - - - - - - - - - - - - - -

	bit	S_SINE,c
	jr	z,w_sine_skip

	ld	de,#sine_h
	ld	a,(de)
	add	a,(hl)
	ld	(de),a
	call	sine
	.if	(H_AMPLITUDE>>6)-1
	.error	"Code depends on 64<=H_AMPLITUDE<128"
	.endif
	sra	a
	sra	a
	add	a,#H_AMPLITUDE/2
	ld	de,(table)
	ld	(de),a

	inc	hl
	ld	de,#sine_v
	ld	a,(de)
	add	a,(hl)
	ld	(de),a
	call	sine
	.if	(V_AMPLITUDE>>6)-1
	.error	"Code depends on 64<=V_AMPLITUDE<128"
	.endif
	sra	a
	sra	a
	add	a,#M_TOP-2+V_AMPLITUDE/2
	ld	(sh_p_top),a
	dec	hl
w_sine_skip:

;- jumping around - - - - - - - - - - - - - - - - - - - - - - - -

	bit	S_JUMP,c
	jr	z,w_jump_skip

	jr	c,w_jump_v

w_jump_h:
	ld	de,(table)
	ld	a,(de)
	add	a,(hl)
	ld	(de),a
	ld	a,(hl)
	neg
	ld	(hl),a
	jr	w_jump_skip

w_jump_v:
	inc	hl
	ld	de,#sh_p_top
	ld	a,(de)
	add	a,(hl)
	ld	(de),a
	ld	a,(hl)
	neg
	ld	(hl),a
	dec	hl
w_jump_skip:

;- moving around  - - - - - - - - - - - - - - - - - - - - - - - -

	bit	S_H_MOVE,c
	jr	z,w_h_move_skip

	ld	de,(table)
	ld	a,(de)
	add	a,(hl)
	ld	(de),a
w_h_move_skip:

	bit	S_V_MOVE,c
	jr	z,w_v_move_skip

	inc	hl
	ld	de,#sh_p_top
	ld	a,(de)
	add	a,(hl)
	ld	(de),a
	dec	hl
w_v_move_skip:

;- adjust the lower gap - - - - - - - - - - - - - - - - - - - - -

	ld	a,(sh_p_top)
	neg
	add	a,#M_BOTTOM+V_AMPLITUDE+M_TOP-2
	ld	(sh_p_bottom),a

;- the state machine  - - - - - - - - - - - - - - - - - - - - - -

	dec	hl
	dec	(hl)
	call	z,inc_script

; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

	call	check_break
	jr	nc,w_quit

	call	b_check
	jp	nz,w_loop	; bottom scroller not finished?

w_quit:
	ld	de,(heap_ptr)
	ld	c,#PIC_HEIGHT
w_ro_olp:
	call	vsync
	xor	a
	ld	b,#BYTE_WIDTH
w_ro_ilp:
	ld	(de),a
	inc	de
	djnz	w_ro_ilp
	dec	c
	jr	nz,w_ro_olp	; roll out logo

	ret

;- Load the next script step into the state machine -------------

inc_script:
	ld	hl,(script_ptr)
	ld	de,#state
	ld	bc,#STATE_SIZE
	ldir
	ld	(script_ptr),hl
	ret

;- HRG screen routine -------------------------------------------

show_hrg:
	.dw	0
sh_p_top	=	.
	.db	0
sh_p_bottom	=	.
	.db	0

	ld	de,(table)
	ld	a,(de)
	inc	de
	call	delay		; horizontal delay
	ld	a,#197+DELAY_OFFSET
	call	delay		; adjustment
	ld	hl,(heap_ptr)
	ld	bc,#BYTE_WIDTH
	jr	sh_start

sh_loop:
	call	sh_line+0x8000	; 17+101
	add	hl,bc		; 11
sh_start:
	ld	a,(de)		; 7
	inc	de		; 6
	sub	#1		; 7
	jr	z,sh_1		; 12/7
	dec	a		; 4
sh_1:
	jr	z,sh_2		; 12/7
	dec	a		; 4
sh_2:
	jr	z,sh_3		; 12/7
	dec	a		; 4
sh_3:
	jr	z,sh_4		; 12/7
	dec	a		; 4
sh_4:
	jr	z,sh_loop	; 12 = 205..209
	ret

sh_line:
	ld	a,h		; 4
	ld	i,a		; 9
	ld	a,l		; 4
	ld	r,a		; 9
	.db	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0	; 16*4
	ret	z		; 11 = 101

;= Data =========================================================

logo:
	.include	"wobb_lg.inc"
PAC_LG_SIZE	=	.-logo
UNP_LG_SIZE	=	UNPACKED
	.if	UNP_LG_SIZE-PIC_HEIGHT*BYTE_WIDTH
	.error	"Wrong size of logo"
	.endif

script:
	.db	0		; action flags
	.db	250		; frames to wait
	.db	0		; horizontal speed
	.db	0		; vertical speed

	; move to center

	.db	M_H_MOVE	; action flags
	.db	H_AMPLITUDE/2	; frames to wait
	.db	+1		; horizontal speed
	.db	0		; vertical speed

	.db	0		; action flags
	.db	200		; frames to wait
	.db	0		; horizontal speed
	.db	0		; vertical speed

	.db	M_V_MOVE	; action flags
	.db	V_AMPLITUDE/2	; frames to wait
	.db	0		; horizontal speed
	.db	+1		; vertical speed

	.db	0		; action flags
	.db	250		; frames to wait
	.db	0		; horizontal speed
	.db	0		; vertical speed

	.db	0		; action flags
	.db	130		; frames to wait
	.db	0		; horizontal speed
	.db	0		; vertical speed

	; swing in a sine wave

	.db	M_SINE		; action flags
	.db	MATH_TWO_PI	; frames to wait
	.db	1		; horizontal speed
	.db	0		; vertical speed

	.db	M_SINE		; action flags
	.db	MATH_TWO_PI/2	; frames to wait
	.db	1		; horizontal speed
	.db	0		; vertical speed

	.db	M_SINE		; action flags
	.db	MATH_TWO_PI	; frames to wait
	.db	1		; horizontal speed
	.db	2		; vertical speed

	.db	M_SINE		; action flags
	.db	MATH_TWO_PI/2	; frames to wait
	.db	1		; horizontal speed
	.db	2		; vertical speed

	.db	M_SINE		; action flags
	.db	MATH_TWO_PI	; frames to wait
	.db	3		; horizontal speed
	.db	6		; vertical speed

	.db	0		; action flags
	.db	25		; frames to wait
	.db	0		; horizontal speed
	.db	0		; vertical speed

	; jump around

	.db	M_H_MOVE|M_V_MOVE	; action flags
	.db	1		; frames to wait
	.db	-(H_AMPLITUDE/2); horizontal speed
	.db	-(PIC_HEIGHT/2)	; vertical speed

	.db	M_JUMP		; action flags
	.db	64*4		; frames to wait
	.db	H_AMPLITUDE	; horizontal speed
	.db	PIC_HEIGHT	; vertical speed

	.db	M_V_MOVE	; action flags
	.db	1		; frames to wait
	.db	0		; horizontal speed
	.db	(PIC_HEIGHT/2)-(V_AMPLITUDE/2)	; vertical speed

	.db	0		; action flags
	.db	80		; frames to wait
	.db	0		; horizontal speed
	.db	0		; vertical speed

	; shift right/left

	.db	M_SH_T		; action flags
	.db	H_AMPLITUDE	; frames to wait
	.db	OFFSET_1L	; horizontal speed
	.db	0		; vertical speed

	.db	M_SH_T		; action flags
	.db	PIC_HEIGHT+10	; frames to wait
	.db	OFFSET_0	; horizontal speed
	.db	0		; vertical speed

	.db	M_SH_T		; action flags
	.db	H_AMPLITUDE	; frames to wait
	.db	OFFSET_1R	; horizontal speed
	.db	0		; vertical speed

	.db	M_SH_T		; action flags
	.db	PIC_HEIGHT+50	; frames to wait
	.db	OFFSET_0	; horizontal speed
	.db	0		; vertical speed

	.db	M_SH_B		; action flags
	.db	H_AMPLITUDE/2	; frames to wait
	.db	OFFSET_2R	; horizontal speed
	.db	0		; vertical speed

	.db	M_SH_B		; action flags
	.db	H_AMPLITUDE/2	; frames to wait
	.db	OFFSET_2L	; horizontal speed
	.db	0		; vertical speed

	.db	M_SH_B		; action flags
	.db	PIC_HEIGHT-H_AMPLITUDE	; frames to wait
	.db	OFFSET_0	; horizontal speed
	.db	0		; vertical speed

	.db	M_SH_T		; action flags
	.db	200		; frames to wait
	.db	OFFSET_0	; horizontal speed
	.db	0		; vertical speed

	; shift right/left and down/up

	.db	M_V_MOVE|M_SH_B	; action flags
	.db	H_AMPLITUDE	; frames to wait
	.db	OFFSET_1R	; horizontal speed
	.db	+1		; vertical speed

	.db	M_V_MOVE|M_SH_B	; action flags
	.db	V_MINUS_H	; frames to wait
	.db	OFFSET_0	; horizontal speed
	.db	+1		; vertical speed

	.db	M_SH_B		; action flags
	.db	V_H_FILL	; frames to wait
	.db	OFFSET_0	; horizontal speed
	.db	0		; vertical speed

	.db	M_SH_B		; action flags
	.db	H_AMPLITUDE	; frames to wait
	.db	OFFSET_1L	; horizontal speed
	.db	0		; vertical speed

	.db	M_SH_B		; action flags
	.db	PIC_HEIGHT	; frames to wait
	.db	OFFSET_0	; horizontal speed
	.db	0		; vertical speed

	.db	M_V_MOVE|M_SH_T	; action flags
	.db	H_AMPLITUDE	; frames to wait
	.db	OFFSET_1L	; horizontal speed
	.db	-1		; vertical speed

	.db	M_V_MOVE|M_SH_T	; action flags
	.db	V_MINUS_H	; frames to wait
	.db	OFFSET_0	; horizontal speed
	.db	-1		; vertical speed

	.db	M_SH_T		; action flags
	.db	V_H_FILL	; frames to wait
	.db	OFFSET_0	; horizontal speed
	.db	0		; vertical speed

	; the wobbling chaos

FR_1	=	(PIC_HEIGHT+1)&~1
	.db	M_W_B		; action flags
	.db	FR_1		; frames to wait
	.db	0		; horizontal speed
	.db	0		; vertical speed

FR_2	=	(H_AMPLITUDE/2+1)&~1
	.db	M_H_MOVE|M_W_B	; action flags
	.db	FR_2		; frames to wait
	.db	-1		; horizontal speed
	.db	0		; vertical speed

FR_3	=	(V_AMPLITUDE/2+1)&~1
	.db	M_V_MOVE|M_W_B	; action flags
	.db	FR_3		; frames to wait
	.db	0		; horizontal speed
	.db	+1		; vertical speed

	.db	M_W_B		; action flags
	.db	FR_4		; frames to wait
	.db	0		; horizontal speed
	.db	0		; vertical speed

	.db	M_SH_B		; action flags
	.db	PIC_HEIGHT	; frames to wait
	.db	OFFSET_0	; horizontal speed
	.db	0		; vertical speed

	.db	M_W_T		; action flags
	.db	PIC_HEIGHT	; frames to wait
	.db	0		; horizontal speed
	.db	0		; vertical speed

	.db	M_SINE|M_W_T	; action flags
	.db	MATH_TWO_PI	; frames to wait
	.db	1		; horizontal speed
	.db	2		; vertical speed

	.db	M_SINE|M_W_T	; action flags
	.db	MATH_TWO_PI	; frames to wait
	.db	1		; horizontal speed
	.db	2		; vertical speed

	.db	M_SH_T		; action flags
	.db	256		; frames to wait
	.db	OFFSET_0	; horizontal speed
	.db	0		; vertical speed

	; The script must be longer than the scroll text!

; This are the offsets for wobbling:
wobble_tab:
	.db	OFFSET_2R
	.db	OFFSET_1R
	.db	OFFSET_1R
	.db	OFFSET_0
	.db	OFFSET_0
	.db	OFFSET_0
	.db	OFFSET_1L
	.db	OFFSET_1L
	.db	OFFSET_2L
	.db	OFFSET_2L
	.db	OFFSET_1L
	.db	OFFSET_1L
	.db	OFFSET_0
	.db	OFFSET_0
	.db	OFFSET_0
	.db	OFFSET_1R
	.db	OFFSET_1R
	.db	OFFSET_2R
WOBBLE_SIZE	=	.-wobble_tab
	.db	OFFSET_END
FR_4	=	WOBBLE_SIZE-(FR_1+FR_2+FR_3)%WOBBLE_SIZE

scroll_line:
	.include	"wobb_sl.inc"
PAC_SL_SIZE	=	.-scroll_line
UNP_SL_SIZE	=	UNPACKED

;= Variables ====================================================

	.area	SCRATCH	(ovr)

table:
	.dw	0

state:
	.db	0		; action flags
	.db	0		; frames to wait
	.db	0		; horizontal speed
	.db	0		; vertical speed
STATE_SIZE	=	.-state

sine_h:
	.db	0
sine_v:
	.db	0

script_ptr:
	.dw	0

wobble_ptr:
	.dw	0

;= Heap usage ===================================================

;		bitmap (initially empty)
HEAP =		PIC_HEIGHT*BYTE_WIDTH
;		HRG control table
HEAP =	HEAP +	1+PIC_HEIGHT
;		unpacked logo bitmap
HEAP =	HEAP +	PIC_HEIGHT*BYTE_WIDTH
;		unpacked scroll text
HEAP =	HEAP +	UNP_SL_SIZE

	.area	HEAP	(abs,ovr)
	.ds	HEAP

;= The end ======================================================
